#include <iostream>

#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <errno.h>
#include <sys/socket.h>
#include <poll.h>

#include "Logger.hpp"
#include "TCPConnection.hpp"
#include "GenericException.hpp"

TCPConnection::TCPConnection(int fd, const IP& remote) {
	this->fd = fd;
	this->remoteAddress = remote;
	closed = false;
	nonBlocking = false;
}

unsigned int TCPConnection::readData() {
	unsigned int Free = buffer.getFree();
	char Tmp[Free];
	ssize_t Read;

	errno = 0;
	Read = read(fd, Tmp, Free);
	if (errno != 0) {
		if (closed) return 0;

		int Errno = errno;
		close();
		GENERICSTRMEXCEPTION("Read failed: " << strerror(Errno));
	}
	if (Read > 0) buffer.put(Tmp, Read);
		else if ((Read == 0) and (!nonBlocking)) {
			/* Connection closed by remote host */
			close();
		}
	return Read;
}

void TCPConnection::sendData(const char* data, unsigned int length) {
	unsigned int Left = length;
	unsigned int Pos = 0;
	while (Left > 0) {
		unsigned int Written;
		Written = write(fd, data + Pos, Left);
		if (Written <= 0) {
			if (closed) return;

			int Errno = errno;
			close();
			GENERICSTRMEXCEPTION("Write failed: " << strerror(Errno));
		}
		Left -= Written;
		Pos += Written;
	}
}

void TCPConnection::sendLine(const std::string& text) {
	sendData(text.c_str(), text.size());
	sendData("\n", 1);
}

void TCPConnection::sendLineNoLB(const std::string &String) {
	sendData(String.c_str(), String.size());
}

void TCPConnection::close() {
	LOGSTRM(Logger::DEBUG, "TCP connection closed FD " << fd);
	closed = true;
	shutdown(fd, 2);
	::close(fd);
}

bool TCPConnection::isClosed() const {
	return closed;
}

bool TCPConnection::haveLine() const {
	return buffer.completeLine();
}

std::string TCPConnection::getNextLine() {
	while (!closed && !haveLine()) {
		readData();
	}
	if (closed) return "";
	return buffer.getLine();
}

void TCPConnection::setNonBlocking() {
	fcntl(fd, F_SETFL, O_NONBLOCK);
	nonBlocking = false;
}

TCPConnection::~TCPConnection() {
	LOGSTRM(Logger::DEBUG, "~TCPConnection");
	close();
}

bool TCPConnection::canReadData() {
	struct pollfd FDs;
	FDs.fd = fd;
	FDs.events = POLLIN;
	FDs.revents = 0;
	return poll(&FDs, 1, 0) == 1;
}

bool TCPConnection::canWriteData() {
	struct pollfd FDs;
	FDs.fd = fd;
	FDs.events = POLLOUT;
	FDs.revents = 0;
	return poll(&FDs, 1, 0) == 1;
}
